package com.alex.statistics.method.spatialAnalysis;

import com.alex.statistics.pojo.request.spatialAnalysis.KernelDensityRequest;
import com.alex.statistics.pojo.result.spatialAnalysis.KernelDensityResult;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.DoubleSummaryStatistics;
import java.util.List;

@Service
public class KernelDensityService {
    private static final double SQRT_2PI = Math.sqrt(2 * Math.PI);

    public KernelDensityResult calculate(KernelDensityRequest request) {
        long startTime = System.currentTimeMillis();

        // 1. 参数校验与准备
        validateRequest(request);
        double bandwidth = request.getBandwidth();
        int gridSize = request.getGridSize();
        List<KernelDensityRequest.Point> points = request.getPoints();

        // 2. 计算数据范围（带5%边界扩展）
        double[] bounds = calculateBounds(points);
        double minX = bounds[0], maxX = bounds[1];
        double minY = bounds[2], maxY = bounds[3];

        // 3. 计算网格实际尺寸
        double cellWidth = (maxX - minX) / gridSize;
        double cellHeight = (maxY - minY) / gridSize;

        // 4. 计算密度矩阵
        double[] densities = computeDensityMatrix(
                points,
                bandwidth,
                gridSize,
                minX, maxX,
                minY, maxY
        );

        // 5. 构建结果
        KernelDensityResult result = new KernelDensityResult();
        result.setBounds(bounds);
        result.setComputeTime(System.currentTimeMillis() - startTime);

        // 6. 生成ECharts热力图数据（原有逻辑保留）
        List<KernelDensityResult.HeatmapPoint> heatmap = new ArrayList<>();
        // 7. 生成OpenLayers热力图数据（新增逻辑）
        List<KernelDensityResult.HeatmapPointOpenlayers> heatmapOpenlayers = new ArrayList<>();

        double minVal = Double.MAX_VALUE;
        double maxVal = Double.MIN_VALUE;

        for (int i = 0; i < gridSize; i++) {
            for (int j = 0; j < gridSize; j++) {
                double value = densities[i * gridSize + j];
                minVal = Math.min(minVal, value);
                maxVal = Math.max(maxVal, value);

                // 计算中心点坐标
                double centerX = minX + i * cellWidth + cellWidth / 2;
                double centerY = minY + j * cellHeight + cellHeight / 2;

                // 计算网格边界
                double minLon = minX + i * cellWidth;
                double maxLon = minX + (i + 1) * cellWidth;
                double minLat = minY + j * cellHeight;
                double maxLat = minY + (j + 1) * cellHeight;

                // 填充ECharts数据（原有）
                KernelDensityResult.HeatmapPoint echartsPoint = new KernelDensityResult.HeatmapPoint();
                echartsPoint.setX(i);
                echartsPoint.setY(j);
                echartsPoint.setValue(value);
                echartsPoint.setRealX(centerX);
                echartsPoint.setRealY(centerY);
                heatmap.add(echartsPoint);

                // 填充OpenLayers数据（新增）
                KernelDensityResult.HeatmapPointOpenlayers olPoint = new KernelDensityResult.HeatmapPointOpenlayers();
                olPoint.setGridX(i);
                olPoint.setGridY(j);
                olPoint.setDensity(value);
                olPoint.setCenterLon(centerX);
                olPoint.setCenterLat(centerY);
                olPoint.setMinLon(minLon);
                olPoint.setMaxLon(maxLon);
                olPoint.setMinLat(minLat);
                olPoint.setMaxLat(maxLat);
                heatmapOpenlayers.add(olPoint);
            }
        }

        result.setHeatmap(heatmap);                // 原有ECharts数据
        result.setHeatmapOpenlayers(heatmapOpenlayers.stream().filter(olPoint -> olPoint.getDensity() > 0.0001).toList());  // 新增OpenLayers数据
        result.setValueRange(new double[]{minVal, maxVal});

        if (request.isIncludeRawMatrix()) {
            result.setRawMatrix(densities);
        }

        return result;
    }

    // 以下方法保持不变
    private double[] computeDensityMatrix(
            List<KernelDensityRequest.Point> points,
            double bandwidth,
            int gridSize,
            double minX, double maxX,
            double minY, double maxY) {

        double[] densities = new double[gridSize * gridSize];
        double cellWidth = (maxX - minX) / gridSize;
        double cellHeight = (maxY - minY) / gridSize;

        for (int i = 0; i < gridSize; i++) {
            double gridX = minX + (i + 0.5) * cellWidth;

            for (int j = 0; j < gridSize; j++) {
                double gridY = minY + (j + 0.5) * cellHeight;
                double sum = 0.0;

                for (KernelDensityRequest.Point point : points) {
                    double dx = (gridX - point.getX()) / bandwidth;
                    double dy = (gridY - point.getY()) / bandwidth;
                    double distanceSq = dx * dx + dy * dy;
                    sum += point.getWeight() * Math.exp(-0.5 * distanceSq) / SQRT_2PI;
                }

                densities[i * gridSize + j] = sum / (points.size() * bandwidth);
            }
        }

        return densities;
    }

    private double[] calculateBounds(List<KernelDensityRequest.Point> points) {
        DoubleSummaryStatistics xStats = new DoubleSummaryStatistics();
        DoubleSummaryStatistics yStats = new DoubleSummaryStatistics();

        points.forEach(p -> {
            xStats.accept(p.getX());
            yStats.accept(p.getY());
        });

        double xRange = xStats.getMax() - xStats.getMin();
        double yRange = yStats.getMax() - yStats.getMin();

        return new double[]{
                xStats.getMin() - 0.05 * xRange,
                xStats.getMax() + 0.05 * xRange,
                yStats.getMin() - 0.05 * yRange,
                yStats.getMax() + 0.05 * yRange
        };
    }

    private void validateRequest(KernelDensityRequest request) {
        if (request.getPoints() == null || request.getPoints().isEmpty()) {
            throw new IllegalArgumentException("点数据不能为空");
        }
        if (request.getBandwidth() <= 0) {
            throw new IllegalArgumentException("带宽必须大于0");
        }
        if (request.getGridSize() <= 0) {
            throw new IllegalArgumentException("网格尺寸必须大于0");
        }
    }
}